/**
 * @file cdnet.h
 * @brief CDNET协议头文件 - Layer 0和Layer 1
 * @details CDNET协议栈的头文件定义，包含协议常量、数据结构、API接口等
 *          Layer 0：8位端口寻址，适用于简单通信场景
 *          Layer 1：16位端口寻址，适用于复杂网络场景
 * 
 * @author 原作者：Duke Fong <d@d-l.io>
 * @author 修改：mastwet@Undefine Innovation
 * @date 2024
 * @copyright MIT License
 * @note 基于Duke Fong的cdnet协议实现
 */

#ifndef CDNET_H
#define CDNET_H

#include <Arduino.h>
#include "cd_list.h"

#ifndef cdn_assert
#define CDN_ERR_ASSERT  -1
#define cdn_assert(expr) { if (!(expr)) return CDN_ERR_ASSERT; }
#endif

/**
 * @defgroup cdnet_constants CDNET协议常量定义
 * @brief CDNET协议使用的常量定义
 * @{
 */

#ifndef CDN_MAX_DAT
/**
 * @brief 最大数据长度
 * @details 允许设置更小的尺寸以节省内存，最大252字节
 */
#define CDN_MAX_DAT         252
#endif

/**
 * @brief 配置标志位定义
 * @details CDN_CONF_NOT_FREE：传输后不释放数据包
 */
#define CDN_CONF_NOT_FREE   (1 << 0)

/**
 * @brief 返回状态码定义
 * @details CDN_RET_FMT_ERR：格式错误，CDN_RET_ROUTE_ERR：路由错误
 */
#define CDN_RET_FMT_ERR     (1 << 1)
#define CDN_RET_ROUTE_ERR   (1 << 2)

/** @} */ // CDNET协议常量定义组结束

/**
 * @defgroup cdnet_enums CDNET枚举类型定义
 * @brief CDNET协议使用的枚举类型
 * @{
 */

/**
 * @brief 多播类型枚举
 * @details 定义CDNET协议的多播类型
 */
typedef enum {
    CDN_MULTI_NONE = 0,      /**< 无多播 */
    CDN_MULTI_CAST,          /**< 组播 */
    CDN_MULTI_NET,           /**< 网络多播 */
    CDN_MULTI_CAST_NET       /**< 组播+网络多播 */
} cdn_multi_t;

/** @} */ // CDNET枚举类型定义组结束

/**
 * @defgroup cdnet_structures CDNET数据结构定义
 * @brief CDNET协议使用的核心数据结构
 * @{
 */

/**
 * @brief CDNET套接字地址结构体
 * @details 定义CDNET协议的网络地址格式
 *          addr[3]格式：[类型, 网络号, MAC地址] 或 [类型, 端口高字节, 端口低字节]
 */
typedef struct {
    uint8_t  addr[3]; /**< 地址数组：[类型, 网络号, MAC地址] */
    uint16_t port;    /**< 端口号 */
} cdn_sockaddr_t;

/**
 * @brief CDNET数据包结构体
 * @details 定义CDNET协议的数据包格式，包含完整的协议头信息
 */
typedef struct {
    list_node_t     node;      /**< 链表节点，用于队列管理 */
    uint8_t         _s_mac;    /**< 源MAC地址 */
    uint8_t         _d_mac;    /**< 目标MAC地址 */
    uint8_t         _l_net;    /**< 本地网络号 */

    uint8_t         conf;      /**< 配置标志位 */
    uint8_t         ret;       /**< 返回状态：bit7=0初始化，bit7=1完成，bit6~0=错误码 */

    cdn_sockaddr_t  src;       /**< 源地址信息 */
    cdn_sockaddr_t  dst;       /**< 目标地址信息 */

    cd_frame_t      *frm;      /**< 关联的CDBUS帧指针 */
    uint8_t         *dat;      /**< 用户数据指针 */
    uint8_t         len;       /**< 用户数据长度 */
} cdn_pkt_t;

/** @} */ // CDNET数据结构定义组结束

/**
 * @defgroup cdnet_compatibility CDNET兼容性定义
 * @brief 向后兼容的宏定义和辅助函数
 * @{
 */

/**
 * @brief 从链表头部获取CDNET数据包
 * @param head 链表头指针
 * @return CDNET数据包指针
 */
#define cdn_list_get(head)               list_get_entry(head, cdn_pkt_t)

/**
 * @brief 将CDNET数据包放入链表
 * @param head 链表头指针
 * @param pkt CDNET数据包指针
 */
#define cdn_list_put(head, pkt)          list_put(head, &(pkt)->node)

/** @} */ // CDNET兼容性定义组结束

/**
 * @defgroup cdnet_api CDNET API接口
 * @brief CDNET协议栈提供的核心API函数
 * @{
 */

/**
 * @brief 根据数据包计算头部大小
 * @param pkt CDNET数据包指针
 * @return 头部大小（字节数）
 */
int cdn_hdr_size_pkt(const cdn_pkt_t *pkt);

/**
 * @brief 根据帧数据计算头部大小
 * @param frm CDBUS帧指针
 * @return 头部大小（字节数）
 */
int cdn_hdr_size_frm(const cd_frame_t *frm);

/**
 * @brief 写入Layer 0协议头部
 * @param pkt CDNET数据包指针
 * @param hdr 头部缓冲区指针
 * @return 写入结果状态码
 */
int cdn0_hdr_w(const cdn_pkt_t *pkt, uint8_t *hdr);

/**
 * @brief 读取Layer 0协议头部
 * @param pkt CDNET数据包指针
 * @param hdr 头部缓冲区指针
 * @return 读取结果状态码
 */
int cdn0_hdr_r(cdn_pkt_t *pkt, const uint8_t *hdr);

/**
 * @brief 写入Layer 1协议头部
 * @param pkt CDNET数据包指针
 * @param hdr 头部缓冲区指针
 * @return 写入结果状态码
 */
int cdn1_hdr_w(const cdn_pkt_t *pkt, uint8_t *hdr);

/**
 * @brief 读取Layer 1协议头部
 * @param pkt CDNET数据包指针
 * @param hdr 头部缓冲区指针
 * @return 读取结果状态码
 */
int cdn1_hdr_r(cdn_pkt_t *pkt, const uint8_t *hdr);

/**
 * @brief 写入Layer 0完整帧
 * @param pkt CDNET数据包指针
 * @return 写入结果状态码
 */
int cdn0_frame_w(cdn_pkt_t *pkt);

/**
 * @brief 读取Layer 0完整帧
 * @param pkt CDNET数据包指针
 * @return 读取结果状态码
 */
int cdn0_frame_r(cdn_pkt_t *pkt);

/**
 * @brief 写入Layer 1完整帧
 * @param pkt CDNET数据包指针
 * @return 写入结果状态码
 */
int cdn1_frame_w(cdn_pkt_t *pkt);

/**
 * @brief 读取Layer 1完整帧
 * @param pkt CDNET数据包指针
 * @return 读取结果状态码
 */
int cdn1_frame_r(cdn_pkt_t *pkt);

/** @} */ // CDNET API接口组结束

/**
 * @defgroup cdnet_utils CDNET工具函数
 * @brief CDNET协议栈提供的辅助工具函数
 * @{
 */

/**
 * @brief 设置网络地址
 * @param addr 地址数组指针
 * @param a0 地址第1字节
 * @param a1 地址第2字节
 * @param a2 地址第3字节
 * @details 设置3字节的网络地址
 */
static inline void cdn_set_addr(uint8_t *addr, uint8_t a0, uint8_t a1, uint8_t a2)
{
    addr[0] = a0;
    addr[1] = a1;
    addr[2] = a2;
}

/**
 * @brief 写入完整帧（自动选择协议类型）
 * @param pkt CDNET数据包指针
 * @return 写入结果状态码
 * @details 根据源地址类型自动选择Layer 0或Layer 1协议
 */
static inline int cdn_frame_w(cdn_pkt_t *pkt)
{
    if (pkt->src.addr[0] == 0x10) // 本地主机
        return -1;
    if (pkt->src.addr[0] == 0x00)
        return cdn0_frame_w(pkt);
    return cdn1_frame_w(pkt);
}

/**
 * @brief 读取完整帧（自动选择协议类型）
 * @param pkt CDNET数据包指针
 * @return 读取结果状态码
 * @details 根据帧数据自动选择Layer 0或Layer 1协议解析
 */
static inline int cdn_frame_r(cdn_pkt_t *pkt)
{
    if (pkt->frm->dat[3] & 0x80)
        return cdn1_frame_r(pkt);
    return cdn0_frame_r(pkt);
}

/** @} */ // CDNET工具函数组结束

#endif // CDNET_H